/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#ifndef CORE_RPCACSREQUEST_H_
#define CORE_RPCACSREQUEST_H_

#include <string>

#include "auth/RpcSignatureComposer.h"
#include "AcsRequest.h"

namespace aliyun {

template<class T>
class RpcAcsRequest : public AcsRequest<T> {
 public:
  // for type traits
  typedef T ResponseType;
  typedef AcsRequest<T> supper;  // parent class

  // for convenience
  typedef std::string string;
  typedef http::MethodType MethodType;
  typedef http::FormatType FormatType;
  typedef auth::RpcSignatureComposer RpcSignatureComposer;

  RpcAcsRequest(string product)
      : supper(product) {
    initialize();
  }

  RpcAcsRequest(string product, string version)
      : supper(product) {
    setVersion(version);
    initialize();
  }

  RpcAcsRequest(string product, string version, string action)
      : supper(product) {
    setVersion(version);
    setActionName(action);
    initialize();
  }

  // shadow parents
  void setActionName(string actionName) {
    supper::setActionName(actionName);
    this->putQueryParameter("Action", actionName);
  }

  void setVersion(string version) {
    supper::setVersion(version);
    this->putQueryParameter("Version", version);
  }

  void setSecurityToken(string securityToken) {
    supper::setSecurityToken(securityToken);
    this->putQueryParameter("SecurityToken", securityToken);
  }

  void setAcceptFormat(FormatType acceptFormat) {
    supper::setAcceptFormat(acceptFormat);
    this->putQueryParameter("Format", acceptFormat.toString());
  }

  // implement
  string compsoeUrl(string endpoint, const std::map<string, string>& queries) {
    std::map<string, string> mapQueries =
        queries.size() != 0 ? queries : this->getQueryParameters();
    string url = this->getProtocol().toString() + "://" + endpoint;
    if (mapQueries.size() == 0) {
      return url;
    }
    if (url.find('?') == string::npos) {
      url += "/?";  // slash for what ?
    }
    return url + supper::concatQueryString(mapQueries);
  }

  // implement
  http::HttpRequest& signRequest(auth::ISigner* signer,
                                 const auth::Credential& credential,
                                 const http::FormatType format,
                                 const regions::ProductDomain& domain) {
    std::map<string, string> mapQueries = this->getQueryParameters();
    if (NULL != signer) {
      string key = credential.getAccessKeyId();
      mapQueries = this->composer_->refreshSignParameters(
          this->getQueryParameters(), signer, key, format);
      mapQueries["RegionId"] = this->getRegionId();

      std::map<string, string> emptyMap;
      string strToSign = this->composer_->composeStringToSign(this->getMethod(),
                                                              "", signer,
                                                              mapQueries,
                                                              emptyMap,
                                                              emptyMap);
      string secret = credential.getAccessSecret();
      string signature = signer->signString(strToSign, secret + "&");
      mapQueries["Signature"] = signature;
    }
    this->setUrl(compsoeUrl(domain.getDomainName(), mapQueries));
    return *this;
  }

 private:
  void initialize() {
    this->setMethod(MethodType::GET);
    this->setAcceptFormat(FormatType::XML);
    this->composer_ = RpcSignatureComposer::getComposer();
  }
};

}  // namespace aliyun

#endif  // CORE_RPCACSREQUEST_H_
